/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.gebze;

import com.asprise.util.jtwain.JTwainException;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import org.gebze.param.Parametre;
import org.gebze.Util.Scanner;

/**
 * Programın Client tarafı
 *
 * @author Özcan ÇAĞIRICI <ozcanca@gebze.bel.tr>
 *
 *
 */
public class GbzClient {

    private ServerSocket serSocket;
    private Socket socket;
    private OutputStream outputStream = null;

    /**
     * Default Constructor. Constructor çağrıldığında program serverdan bağlantı
     * beklemeye başlar.
     */
    public GbzClient() {
        try {
            serSocket = new ServerSocket(Parametre.PORT_NO);
            serSocket.setSoTimeout(0);
            waitServer();
        } catch (IOException ex) {
            System.out.println("Client başlatilamadi: "+ex);
        }
    }

    /**
     * serverdan bağlantı bekler, bağlantı gelince dosya işlemlerini başlatır
     */
    private void waitServer() {
        try {
            socket = serSocket.accept();
            outputStream = socket.getOutputStream();

        } catch (IOException ex) {
            System.out.println("Server ile baglanti kurulmadi:" + ex);
        }
        boolean islemBittiMi = dosyaTaraGonder();
        if (islemBittiMi) {
            System.out.println("Dosya Gonderme Islemi Basarili");
        } else {
            System.out.println("Dosya Gonderme Islemi Basarisiz");
        }

    }

    /**
     * belgeyi taramayı başlatır. Belge boyutunu karsi tarafa gonderir. taranan
     * belgeyi parçalayıp gönderen metodu çalıştırır.
     *
     * @param outputStream
     */
    private boolean dosyaTaraGonder() {
        byte[] okunanBelge = startScan();
        if (okunanBelge != null) {
            Integer boyut = okunanBelge.length;
            StringBuilder sb = new StringBuilder(boyut.toString());
            sb.append("\n");
            try {
                outputStream.write(sb.toString().getBytes());
                outputStream.flush();
            } catch (IOException ex) {
                System.out.println("Dosya boyutu karşıya gonderilemedi:" + ex);
                return false;
            }
            System.out.println("Gonderilecek toplam data boyutu: " + boyut);

            return imageParcalaGonder(okunanBelge);
        }
        return false;
    }

    /**
     * Tarayıcı işlemlerini yapar, geriye taranan belgenin byte [] halini
     * döndürür.
     *
     * @return
     */
    private byte[] startScan() {
        byte[] belge = null;
        Scanner scanner = null;

        try {
            scanner = new Scanner();
            belge = scanner.getImage();

        } catch (JTwainException ex) {
            System.out.println("Tarama sirasinda hata meydana geldi: " + ex);
            belge = null;
        }
        return belge;
    }

    /**
     * Taranan belgeyi parcalara ayirir.Karsi tarafa (server) gonderir.
     *
     * @param belge
     *
     */
    private boolean imageParcalaGonder(byte[] belge) {

        int tamParcaSayisi = belge.length / Parametre.MAX_DATA_PARCA_BOYUTU;
        int artan = belge.length % Parametre.MAX_DATA_PARCA_BOYUTU;
        byte[] artanDizi = new byte[artan + Parametre.PARCA_HEADER_SIZE];
        byte[] parca = new byte[Parametre.MAX_PARCA_BOYUTU];


        for (int i = 0; i < tamParcaSayisi; ++i) {
            parca = getPart(i, Parametre.MAX_DATA_PARCA_BOYUTU, belge);
            try {

                outputStream.write(parca);
                outputStream.flush();

            } catch (IOException ex) {
                System.out.println(i + ". data parcasi gonderilemedi: " + ex);
                return false;
            }
        }

        artanDizi = getPart(tamParcaSayisi, artan, belge);
        try {
            outputStream.write(artanDizi);
        } catch (IOException ex) {
            System.out.println("Son data parcasi gonderilemedi: " + ex);
            return false;
        }
        return true;
    }

    /**
     * verilen parçanın başına header bilgisini ekleyerek yeni parçayı döndürür.
     *
     * @param siraSayisi
     * @param okunacakBoyut
     * @param kaynak
     * @return
     */
    private byte[] getPart(int siraSayisi, int okunacakBoyut, byte[] kaynak) {
        int k = 0;
        byte[] parca = new byte[Parametre.MAX_PARCA_BOYUTU];
        byte[] diziSirasi = convertIntTo32Bits(siraSayisi);

        for (int i = 0; i < Parametre.PARCA_HEADER_SIZE; ++i) {
            parca[k++] = diziSirasi[i];
        }
        for (int j = siraSayisi * Parametre.MAX_DATA_PARCA_BOYUTU; j < Parametre.MAX_DATA_PARCA_BOYUTU * siraSayisi + okunacakBoyut; ++j) {
            parca[k++] = kaynak[j];
        }
        return parca;
    }

    /**
     * Integer degerleri, byte arraye donusturur.
     *
     * @param value
     * @return
     */
    private byte[] convertIntTo32Bits(int value) {
        String s = Integer.toBinaryString(value);
        byte[] retValue = new byte[Parametre.PARCA_HEADER_SIZE];
        int fark = Parametre.PARCA_HEADER_SIZE - s.length();
        for (int i = 0; i < fark; ++i) {
            retValue[i] = Byte.parseByte("0");
        }
        for (int i = fark; i < Parametre.PARCA_HEADER_SIZE; ++i) {
            if (s.charAt(i - fark) == 48) {
                retValue[i] = Byte.parseByte("0");
            } else {
                retValue[i] = Byte.parseByte("1");
            }
        }
        return retValue;


    }
}
